package edu.wpi.cs509.ui;

import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.border.Border;

import edu.wpi.cs509.representation.PlayerPiece;
import edu.wpi.cs509.representation.StrategoPiece;
import edu.wpi.cs509.representation.TeamColor;

/**
 * Graveyard implements the portion of the game board window where 
 * pieces that are not in play are located.  There are two of these
 * objects in each game board window, one for the red player, and
 * one for the blue player.  This is also the location where the 
 * player names are displayed.
 * 
 * @author afoltan
 *
 */
@SuppressWarnings("serial")
public class Graveyard extends JPanel {

	private static final int MY_TURN_BLINK_COUNT = 16;
	
	// player name suitable for display
	private GraveyardPlayerName playerName;

	// the color of the player (RED or BLUE)
	private TeamColor teamColor;
	
	// a mapping of stratego piece to graphic panel
	private HashMap<StrategoPiece, GraveyardPlot> graves;
	
	// constant for the graphic border width (in pixels)
	private static final int BORDER = 5;
	
	/**
	 * The opaque graveyard constructor
	 * 
	 * This constructor will create a graveyard that shows no piece values.
	 * Only a single hidden piece will be displayed, with a total count.  This
	 * is to be used during initial piece placement to represent the opponents
	 * graveyard as they make their initial piece placement.
	 * 
	 * All pieces in this graveyard are of type StrategoPiece.HIDDEN.
	 * 
	 * There are no mouse event notifications supported from this graveyard.
	 * 
	 * @param name - the name of the player or "Waiting for player ..." message
	 * @param TeamColor - the color representing the side the player is on
	 */
	public Graveyard( String name, TeamColor teamColor ) {

		this.teamColor = teamColor;
		
		// use a centered layout
		setLayout( new GraveyardLayout(true) );
		
		// Player name panel
		playerName = new GraveyardPlayerName( teamColor, name );
		add( playerName );
		playerName.setVisible(true);

		// Add the hidden piece to the graveyard
		GraveyardPlot g;
		g = new GraveyardPlot(this, StrategoPiece.HIDDEN, StrategoPiece.HIDDEN.numberPerUser(), null);
		graves = new HashMap<StrategoPiece, GraveyardPlot>();
		graves.put(StrategoPiece.HIDDEN, g);
		add( g );
		g.setVisible(true);
		
		// background
		if (teamColor == TeamColor.BLUE) {
			setBackground( Color.BLUE );
		} else {
			setBackground( Color.RED );
		}
		setOpaque(true);
		
		// show it
		setVisible(true);
	}
	
	/**
	 * The graveyard constructor
	 * 
	 * This constructor creates a graveyard of pieces for the specified team color, initialized
	 * with the starting piece in piecesList.  Any mouse events occurring in the graveyard will
	 * be notified through the GraveyardNotify interface. 
	 * 
	 * @param name - the name of the player or "Waiting for player ..." message
	 * @param TeamColor - the color representing the side the player is on
	 * @param event - the event handler that will be notified of mouse events originating in the graveyard
	 * @param startEmpty - when true, the graveyard should initialize itself with 0 pieces;  false will 
	 *                initialize the graveyard with the starting piece count
	 */
	public Graveyard( String name, TeamColor teamColor, GraveyardNotify event, boolean startEmpty ) {
		
		this.teamColor = teamColor;
		
		// set the border
		Border border = BorderFactory.createEmptyBorder(BORDER,BORDER,BORDER,BORDER);
		setBorder(border);

		// use a custom layout
		setLayout( new GraveyardLayout() );
		
		// Player name panel
		playerName = new GraveyardPlayerName( teamColor, name );
		playerName.setMinimumSize( playerName.getPreferredSize() );
		playerName.setMaximumSize( playerName.getPreferredSize() );
		add( playerName );
		playerName.setVisible(true);
		
		// add each piece to the graveyard
		// the graveyard area consisting of 'plots'
		graves = new HashMap<StrategoPiece, GraveyardPlot>();

		GraveyardPlot g;
		int count;
		
		for (StrategoPiece p : StrategoPiece.ALL_GAME_PIECES) {
			count = startEmpty ? 0 : p.numberPerUser();
			g = new GraveyardPlot(this, p, count, event);
			graves.put(p, g);
			add( g );			
		}
		
		// background
		if (teamColor == TeamColor.BLUE) {
			setBackground( Color.BLUE );
		} else {
			setBackground( Color.RED );
		}
		setOpaque(true);
		
		// show it
		setVisible(true);
	}
	
	/**
	 * Retrieves the name of the player owning this graveyard
	 * @return
	 */
	public GraveyardPlayerName getPlayerName() {
		return playerName;
	}
	
	/**
	 * Retrieves the graveyard plot for the specified Stratego piece
	 * @param p - the piece to locate the plot for
	 * @return - the GraveyardPlot of that piece
	 */
	protected GraveyardPlot getGraveyardPlot( StrategoPiece p ) {
		return graves.get(p);
	}
	
	/**
	 * This method allows the caller to deselect all graveyard plots
	 * that may be currently highlighted as a result of a mouse event
	 */
	public void unselectAll() {
		for (GraveyardPlot g : graves.values()) {
			g.setSelected(false);
		}
	}
	
	/**
	 * Sets the players name and updates the graveyard panel
	 * @param name
	 */
	public void setPlayerName( String name ) {
		playerName.setPlayerName(name);
	}
	
	/**
	 * Sets the count of the specified piece in the graveyard
	 * @param piece - the piece to change the count of
	 * @param count - the new count
	 */
	public void setPieceCount( StrategoPiece piece, int count ) {
		// locate graveyard plot
		GraveyardPlot plot = graves.get(piece);
		if (plot == null) {
			System.out.println("Graveyard set for piece "+piece.getName()+" which doesn't exist - ignored");
			return;
		}

		plot.setCount(count);
	}

	/**
	 * Retrieves the count of pieces in the graveyard for the specified piece
	 * @param piece - the piece to retrieve the count
	 * @return - the count
	 */
	public int getPieceCount( StrategoPiece piece ) {
		// locate graveyard plot
		GraveyardPlot plot = graves.get(piece);
		if (plot == null) {
			System.out.println("Graveyard queried for piece "+piece.getName()+" which doesn't exist");
			System.out.println("Returning number-per-user instead");
			return piece.numberPerUser();
		}
		return plot.getCount();
	}
	
	/**
	 * Retrieves the total count of pieces in the graveyard for all pieces
	 * @param piece - the piece to retrieve the count
	 * @return - the count
	 */
	public int getTotalPieceCount() {
		// locate graveyard plot
		int count=0;
		for(GraveyardPlot g:graves.values()) {
		    count += g.getCount();
		}
		System.out.println(count);
		return count;
	}
	/**
	 * Retrieves the color of this graveyard
	 * @return - the TeamColor (RED or BLUE)
	 */
	public TeamColor getColor() {
		return teamColor;
	}
	
	/**
	 * Invoked when it's the players turn to indicate that it is their
	 * turn to move.
	 */
	public void setMyTurn() {
		playerName.setMyTurn(MY_TURN_BLINK_COUNT);
	}

	/**
	 * Invoked when the players turn has ended to restore their username
	 * to un-highlighted state
	 */
	public void clearMyTurn() {
		playerName.clearMyTurn();
	}
	
	/**
	 * Retrieves a list of pieces remaining in the graveyard
	 */
	public List<PlayerPiece> getRemainingPieces() {
		List<PlayerPiece> list = new ArrayList<PlayerPiece>();
		for (GraveyardPlot g : graves.values()) {
			int count = g.getCount();
			while (count > 0) {
				list.add( new PlayerPiece( g.getStrategoPiece(), g.getPlayerColor()) );
				count--;
			}
		}
		return list;
	}

	/**
	 * Restores the graveyard piece counts to the initial game setup counts
	 * @Note for an obfuscated graveyard, the count is restored to 40
	 */
	public void resetPieceCounts() {
		for (GraveyardPlot g : graves.values()) {
			g.setCount( g.getStrategoPiece().numberPerUser() );
		}
	}
	
}
